local brain = require "brains/musha_moontreebrain"

local assets = {
	Asset("ANIM", "anim/leif_walking.zip"),
	Asset("ANIM", "anim/leif_actions.zip"),
	Asset("ANIM", "anim/leif_attacks.zip"),
	Asset("ANIM", "anim/leif_idles.zip"),
	Asset("SOUND", "sound/leif.fsb"),
	Asset("ANIM", "anim/musha_moontree_build.zip"),
	Asset("ANIM", "anim/musha_moontree_spring_build.zip"),
	Asset("ANIM", "anim/musha_moontree_fall_build.zip"),
	Asset("ANIM", "anim/musha_moontree_winter_build.zip")
}

local prefabs = {
	"livinglog",
	"musha_beefalo_egg"
}

local chanceloottable = {
	{
		{"livinglog", 1},
		{"livinglog", 0.75},
		{"monstermeat", 1}
	},
	{
		{"livinglog", 1},
		{"livinglog", 1},
		{"livinglog", 0.75},
		{"monstermeat", 1}
	},
	{
		{"livinglog", 1},
		{"livinglog", 1},
		{"livinglog", 0.75},
		{"monstermeat", 1},
		{"monstermeat", 0.75}
	},
	{
		{"livinglog", 1},
		{"livinglog", 1},
		{"livinglog", 1},
		{"livinglog", 1},
		{"livinglog", 0.75},
		{"monstermeat", 1},
		{"monstermeat", 1},
		{"monstermeat", 1},
		{"monstermeat", 0.75}
	},
	{
		{"livinglog", 1},
		{"livinglog", 1},
		{"livinglog", 1},
		{"livinglog", 1},
		{"livinglog", 1},
		{"monstermeat", 1},
		{"monstermeat", 1},
		{"monstermeat", 1}
	}
}

local function order(inst, commander, action)
	if inst.ownerid == commander.userid then
		if not inst:IsNear(commander, 20) then
			commander.components.talker:Say(STRINGS.MUSHA_TALK_ORDER_DALL_LOST)
			return
		end
		local followers = deepcopy(inst.components.leader.followers)
		inst.components.leader:RemoveAllFollowers()
		if action == "follow" then
			inst.components.sleeper:WakeUp()
			inst.components.follower:SetLeader(commander)
			commander.components.talker:Say(STRINGS.MUSHA_TALK_ORDER_DALL_FOLLOW)
			for k, v in pairs(followers) do
				TheWorld:DoTaskInTime(
					math.random() * 1.2,
					function()
						local musha_moondrake = ReplacePrefab(k, "musha_moondrake")
						musha_moondrake.components.follower:SetLeader(inst)
					end
				)
			end
		elseif action == "stay" then
			inst.components.follower:SetLeader(nil)
			inst.components.sleeper:GoToSleep()
			commander.components.talker:Say(STRINGS.MUSHA_TALK_ORDER_DALL_STAY)
			for k, v in pairs(followers) do
				TheWorld:DoTaskInTime(
					math.random() * 1.2,
					function()
						k.components.sleeper:GoToSleep()
						k.components.locomotor:StopMoving()
						if not k.sg:HasStateTag("hidden") then
							k.sg:GoToState("enter")
						end
						local plant =
							ReplacePrefab(
							k,
							RandomChoice(
								{
									"carrot_planted",
									"berrybush",
									"green_mushroom",
									"blue_mushroom",
									"cactus",
									"flower_cave"
								}
							)
						)
						plant:AddComponent("follower")
						plant.components.follower:SetLeader(inst)
						plant.components.pickable.onpickedfn = plant.Remove
					end
				)
			end
		end
	end
end

local function ondeath(inst)
	SpawnPrefab("statue_transition_2").Transform:SetPosition(inst:GetPosition():Get())
	if inst.components.container then
		inst.components.container:DropEverything()
	end
end

---------------------------
local function Upgrade(inst, exp)
	local level_old = inst.level
	inst.exp = math.min(inst.exp + exp, inst.max_exp)
	if inst.exp == 2500 then
		inst.level = 10
	elseif inst.exp > 2000 then
		inst.level = 9
		inst.components.lootdropper:SetChanceLootTable(chanceloottable[5])
	elseif inst.exp > 1550 then
		inst.level = 8
	elseif inst.exp > 1100 then
		inst.level = 7
		inst.components.lootdropper:SetChanceLootTable(chanceloottable[4])
	elseif inst.exp > 700 then
		inst.level = 6
	elseif inst.exp > 450 then
		inst.level = 5
		inst.components.lootdropper:SetChanceLootTable(chanceloottable[3])
	elseif inst.exp > 270 then
		inst.level = 4
	elseif inst.exp > 120 then
		inst.level = 3
		inst.components.lootdropper:SetChanceLootTable(chanceloottable[2])
	elseif inst.exp > 50 then
		inst.level = 2
	else
		inst.level = 1
		inst.components.lootdropper:SetChanceLootTable(chanceloottable[1])
	end
	if inst.level ~= level_old then
		local health_percent = inst.components.health:GetPercent()
		local scale = inst.level * 0.1 + 0.45
		inst.Transform:SetScale(scale, scale, scale)
		inst.components.health.maxhealth = math.ceil(450 + inst.level * inst.level * 30)
		inst.components.health:SetPercent(health_percent)
	end
end

local function onkill(inst, data)
	local victim = data.victim
	if victim and victim.components.combat then
		Upgrade(inst, victim.components.combat.defaultdamage)
	end
end

local function ShouldAcceptItem(inst, item)
	return inst.components.eater:CanEat(item) and not inst.components.combat:HasTarget()
end

local function OnGetItemFromPlayer(inst, giver, item)
	if inst.components.eater:CanEat(item) then
		inst.components.eater:Eat(item)
	end
end

local function OnRefuseItem(inst, item)
	if not inst.sleep_on and inst.AnimState:AnimDone() then
		inst.sg:GoToState("panic")
	end
end

local function OnEat(inst, food)
	local food_value =
		food.components.edible and
		3 * food.components.edible.hungervalue + 2 * food.components.edible.sanityvalue +
			5 * food.components.edible.healthvalue or
		40
	Upgrade(inst, food_value)
end

local function flower_shield(inst, attacked, data)
	if not inst.components.health:IsDead() then
		if inst.components.health:GetPercent() >= .8 then
			SpawnPrefab("green_leaves").Transform:SetPosition(inst:GetPosition():Get())
		elseif inst.components.health:GetPercent() < .8 and inst.components.health:GetPercent() >= .6 then
			SpawnPrefab("yellow_leaves").Transform:SetPosition(inst:GetPosition():Get())
		elseif inst.components.health:GetPercent() < .6 and inst.components.health:GetPercent() >= .4 then
			SpawnPrefab("orange_leaves").Transform:SetPosition(inst:GetPosition():Get())
		elseif inst.components.health:GetPercent() < .4 then
			SpawnPrefab("red_leaves").Transform:SetPosition(inst:GetPosition():Get())
		elseif inst.components.health:GetPercent() < .2 then
			inst.components.combat:SetTarget(nil)
			inst.components.combat:GiveUp()
		end
	end
end

local function OnOpen(inst)
	if not inst.components.health:IsDead() then
		inst.components.combat:SetTarget(nil)
		inst.components.combat:GiveUp()
		if not inst.sleep_on then
			inst.sg:GoToState("hit")
			SpawnPrefab("green_leaves_chop").Transform:SetPosition(inst:GetPosition():Get())
		elseif inst.sleep_on then
			SpawnPrefab("green_leaves_chop").Transform:SetPosition(inst:GetPosition():Get())
		end
		inst.SoundEmitter:PlaySound("dontstarve/beefalo/saddle/shake_off")
	end
	if inst.components.burnable:IsBurning() then
		inst.components.burnable:Extinguish()
	end
	if inst.components.freezable:IsFrozen() then
		inst.components.freezable:Unfreeze()
	end
end

local function OnClose(inst)
	if not inst.components.health:IsDead() then
		inst.SoundEmitter:PlaySound("dontstarve/beefalo/saddle/shake_off")

		if not inst.sleep_on then
			inst.sg:GoToState("hit")
			SpawnPrefab("pine_needles_chop").Transform:SetPosition(inst:GetPosition():Get())
		elseif inst.sleep_on then
			SpawnPrefab("pine_needles_chop").Transform:SetPosition(inst:GetPosition():Get())
		end
	end
end

local function OnHitnature(inst, data)
	local other = data.target

	if
		other and
			(other:HasTag("_musha") or
				inst.components.follower.leader ~= nil and other.components.follower and
					other.components.follower.leader == inst.components.follower.leader)
	 then
		inst.components.combat:SetTarget(nil)
		inst.components.combat:GiveUp()
	end
end

local function Retarget(inst)
	return FindEntity(
		inst,
		30,
		function(guy)
			return guy.prefab ~= inst.prefab and guy.entity:IsVisible() and not guy.components.health:IsDead() and
				guy ~= (inst or inst.components.follower.leader) and
				not (not guy.components.follower or
					(guy.components.follower.leader and guy.components.follower.leader == inst.components.follower.leader)) and
				not guy:HasTag("player") and
				not guy:HasTag("companion") and
				not guy:HasTag("wall")
		end,
		{"_combat", "_health"},
		{"prey"}
	)
end

local function on_follow(inst, data)
	inst.active_task =
		inst:DoPeriodicTask(
		1,
		function(inst)
			inst.components.hunger:DoDelta(-0.2)
			if inst.components.hunger:GetPercent() < 0.05 then
				inst.components.follower:SetLeader(nil)
				inst.components.sleeper:GoToSleep()
			end
		end
	)
end

local function on_close(inst)
	if inst.components.follower.leader then
		inst.components.locomotor.walkspeed = 4.5
	end
end

local function on_far(inst)
	if inst.components.follower.leader then
		inst.components.locomotor.walkspeed = 4.5
	end
end

local function Sleeping(inst, data)
	inst.recoverytask =
		inst:DoPeriodicTask(
		1,
		function(inst)
			inst.components.health:DoDelta(1)
		end
	)
	if TheWorld.state.isday or TheWorld.state.iscaveday then
		if TheWorld.state.isspring then
			inst.AnimState:SetBuild("musha_moontree_spring_build")
			SpawnPrefab("green_leaves").Transform:SetPosition(inst:GetPosition():Get())
		elseif TheWorld.state.issummer then
			inst.AnimState:SetBuild("musha_moontree_build")
			SpawnPrefab("green_leaves").Transform:SetPosition(inst:GetPosition():Get())
		elseif TheWorld.state.isautumn then
			inst.AnimState:SetBuild("musha_moontree_fall_build")
			SpawnPrefab("red_leaves").Transform:SetPosition(inst:GetPosition():Get())
		elseif TheWorld.state.iswinter then
			inst.AnimState:SetBuild("musha_moontree_winter_build")
			SpawnPrefab("yellow_leaves").Transform:SetPosition(inst:GetPosition():Get())
		end
	end
end

local function OnBurnt(inst)
	if inst.components.propagator and inst.components.health and not inst.components.health:IsDead() then
		inst.components.propagator.acceptsheat = true
	end
end

local function InShadow(inst, data)
	local x, y, z = inst.Transform:GetWorldPosition()
	local ents = TheSim:FindEntities(x, y, z, 15)
	for k, v in pairs(ents) do
		if
			inst.sleep_on and v.components.combat and v.components.combat.target == inst and
				not (v:HasTag("berrythief") or v:HasTag("prey") or v:HasTag("bird") or v:HasTag("butterfly"))
		 then
			v.components.combat.target = nil
			inst:DoTaskInTime(
				3,
				function()
					inst.sg:GoToState("sleeping")
				end
			)
		end
	end
end

local function OnAttacked(inst, data)
	if data.attacker and data.attacker:HasTag("_musha") or data.attacker:HasTag("player") then
		inst.components.combat:SetTarget(nil)
		inst.components.combat:GiveUp()
	end
	if inst.components.sleeper.isasleep then
		InShadow(inst)
		inst.sg:GoToState("sleeping")
	else
		inst.components.combat:SetTarget(data.attacker)
	end
end

local function OnLoad(inst, data)
	if data then
		inst.ownerid = data.ownerid
		Upgrade(inst, data.exp or 0)
	end
end

local function OnSave(inst, data)
	data.exp = inst.exp
	data.ownerid = inst.ownerid
end

local function summon_followers(inst)
	if TheWorld.state.isdusk then
		local x, y, z = inst.Transform:GetWorldPosition()
		local offset =
			FindWalkableOffset(
			inst:GetPosition(),
			math.random(1, 360) * DEGREES,
			math.random(2, TUNING.DECID_MONSTER_TARGET_DIST),
			30,
			false,
			false
		)
		local new_follower =
			SpawnPrefab(
			RandomChoice(
				{
					"carrot_planted",
					"berrybush",
					--"berrybush2",
					--"berrybush_juicy",
					"green_mushroom",
					"blue_mushroom",
					"cactus",
					"flower_cave"
				}
			)
		)
		new_follower.Transform:SetPosition(x + offset.x, y + offset.y, z + offset.z)
		--new_follower.AnimState:PlayAnimation("planted")
		new_follower:AddComponent("follower")
		new_follower.components.follower:SetLeader(inst)
		new_follower.components.sleeper:GoToSleep()
	end
end

local function change_look(inst)
	if TheWorld.state.isspring then
		inst.AnimState:SetBuild("musha_moontree_spring_build")
		SpawnPrefab("green_leaves").Transform:SetPosition(inst:GetPosition():Get())
	elseif TheWorld.state.issummer then
		inst.AnimState:SetBuild("musha_moontree_build")
		SpawnPrefab("green_leaves").Transform:SetPosition(inst:GetPosition():Get())
	elseif TheWorld.state.isautumn then
		inst.AnimState:SetBuild("musha_moontree_fall_build")
		SpawnPrefab("red_leaves").Transform:SetPosition(inst:GetPosition():Get())
	elseif TheWorld.state.iswinter then
		inst.AnimState:SetBuild("musha_moontree_winter_build")
		SpawnPrefab("yellow_leaves").Transform:SetPosition(inst:GetPosition():Get())
	end
end

local function fn(build)
	local inst = CreateEntity()

	inst.entity:AddTransform()
	inst.entity:AddAnimState()
	inst.entity:AddSoundEmitter()
	inst.entity:AddDynamicShadow()
	inst.entity:AddNetwork()
	inst.entity:AddLightWatcher()

	MakeCharacterPhysics(inst, 1000, .5)

	inst.DynamicShadow:SetSize(4, 1.5)
	inst.Transform:SetFourFaced()

	inst:AddTag("musha_follower")
	inst:AddTag("musha_moontree")
	inst:AddTag("notraptrigger")
	inst:AddTag("companion")
	inst:AddTag("shelter")
	inst:AddTag("musha_slave")
	inst:AddTag("epic")
	--inst:AddTag("leif")
	inst:AddTag("tree")
	inst:AddTag("evergreens")
	inst:AddTag("largecreature")

	inst.entity:AddMiniMapEntity()
	inst.MiniMapEntity:SetIcon("musha_moontree.tex")
	inst.MiniMapEntity:SetPriority(10)
	inst.MiniMapEntity:SetDrawOverFogOfWar(true)
	inst.MiniMapEntity:SetCanUseCache(false)

	inst.AnimState:SetBank("leif")
	change_look(inst)
	inst.AnimState:PlayAnimation("idle_loop", true)

	inst:AddComponent("talker")
	inst.components.talker.fontsize = 30
	inst.components.talker.colour = Vector3(0.85, 0.65, 0.95, 1)
	inst.components.talker.offset = Vector3(0, -800, 0)

	inst.entity:SetPristine()

	if not TheWorld.ismastersim then
		inst.OnEntityReplicated = function(inst)
			inst.replica.container:WidgetSetup("musha_chest_4")
		end
		return inst
	end

	inst.exp = 0
	inst.max_exp = 2500
	inst.level = 0

	inst:AddComponent("container")
	inst.components.container:WidgetSetup("musha_chest_4")
	inst.components.container.onopenfn = OnOpen
	inst.components.container.onclosefn = OnClose
	------------------------------------------

	inst:AddComponent("locomotor")
	inst.components.locomotor.walkspeed = 1.5

	------------------------------------------
	inst:SetStateGraph("SGLeif")
	------------------------------------------
	inst:AddComponent("sanityaura")
	inst.components.sanityaura.aura = TUNING.SANITYAURA_TINY

	MakeLargeBurnableCharacter(inst, "marker")
	inst.components.burnable.flammability = TUNING.LEIF_FLAMMABILITY
	inst.components.burnable:SetOnBurntFn(OnBurnt)
	inst.components.propagator.acceptsheat = true

	MakeHugeFreezableCharacter(inst, "marker")
	------------------
	inst:AddComponent("health")
	inst.components.health:SetMaxHealth(450)

	inst:AddComponent("hunger")
	inst.components.hunger:SetMax(250)
	inst.components.hunger:SetRate(0)
	------------------
	inst:AddComponent("eater")
	inst.components.eater:SetDiet({FOODTYPE.VEGGIE, FOODTYPE.ROUGHAGE}, {FOODTYPE.VEGGIE, FOODTYPE.ROUGHAGE})
	inst.components.eater:SetOnEatFn(OnEat)

	inst:AddComponent("trader")
	inst.components.trader:SetAcceptTest(ShouldAcceptItem)
	inst.components.trader.onaccept = OnGetItemFromPlayer
	inst.components.trader.onrefuse = OnRefuseItem

	inst:AddComponent("combat")
	inst.components.combat.hiteffectsymbol = "marker"
	inst.components.combat:SetDefaultDamage(TUNING.LEIF_DAMAGE)
	inst.components.combat:SetAttackPeriod(TUNING.LEIF_ATTACK_PERIOD)
	inst.components.combat:SetRetargetFunction(1, Retarget)
	inst.components.combat.playerdamagepercent = 0
	inst:ListenForEvent("attacked", flower_shield)
	inst:ListenForEvent("onhitother", OnHitnature)
	--inst:AddComponent("deciduoustreeupdater")

	inst:AddComponent("leader")
	inst:AddComponent("follower")
	inst.components.follower:KeepLeaderOnAttacked()
	inst.components.follower.keepdeadleader = true

	MakeHauntableIgnite(inst)

	inst:AddComponent("named")
	inst.components.named.possiblenames = STRINGS.DALL
	inst.components.named:PickNewName()

	inst:AddComponent("maprevealer")
	inst.components.maprevealer.revealperiod = 0.5
	inst.components.maprevealer:Start()

	inst:AddComponent("playerprox")
	inst.components.playerprox:SetDist(12, 12)
	inst.components.playerprox:SetOnPlayerNear(on_close)
	inst.components.playerprox:SetOnPlayerFar(on_far)

	inst:AddComponent("sleeper")
	inst.components.sleeper:SetResistance(80)
	inst.components.sleeper.testperiod = 15
	inst.components.sleeper:SetSleepTest(on_follow)

	if not MUSHA_SETTINGS.no_followers then
		inst.components.sleeper:SetWakeTest(summon_followers)
	end
	inst.components.sleeper:GoToSleep()

	inst:AddComponent("lootdropper")

	inst:AddComponent("inspectable")
	inst.components.inspectable:RecordViews()

	inst:ListenForEvent("killed", onkill)
	inst.Transform:SetScale(0.7, 0.7, 0.7)
	inst:ListenForEvent("attacked", OnAttacked)
	inst:ListenForEvent("death", ondeath)
	inst:ListenForEvent("gotosleep", Sleeping)
	inst:DoPeriodicTask(
		0.5,
		function(inst)
			if inst.LightWatcher:IsInLight() then
				inst.components.hunger:DoDelta(0.1)
			end
		end
	)
	inst:ListenForEvent(
		"onbuilt",
		function(inst, data)
			data.builder.components.musha_leader:AddFollower("musha_moontree", inst)
		end
	)
	inst:WatchWorldState(
		"season",
		function(inst, season)
			change_look(inst)
		end
	)

	inst.OnSave = OnSave
	inst.OnLoad = OnLoad
	inst.order = order

	inst:SetBrain(brain)

	return inst
end

return Prefab("musha_moontree", fn, assets, prefabs), MakePlacer(
	"musha_moontree_placer",
	"leif",
	"musha_moontree_build",
	"idle"
)
